"""
重构后的LLM服务主类
负责协调各个子模块完成测试用例生成
"""

import logging
import asyncio
import time
from typing import List, Dict, Any, Optional
from app.models.schemas import TestCaseRequest, TestCaseResponse
from app.services.requirement_analysis_service import RequirementAnalysisService
from app.services.model_adapters import ModelFactory
from .prompt_manager import PromptManager
from .response_parser import ResponseParser
from .timeout_manager import TimeoutManager

logger = logging.getLogger(__name__)


class LLMService:
    """重构后的LLM服务类"""

    def __init__(self):
        from app.config.settings import get_settings
        self.settings = get_settings()

        # 初始化子模块
        self.prompt_manager = PromptManager()
        self.response_parser = ResponseParser()
        self.timeout_manager = TimeoutManager(self.settings)
        self.analysis_service = RequirementAnalysisService()

        # 初始化模型适配器
        self._init_model_adapter()

        # 并发处理器（延迟导入）
        self.concurrent_processor = None

    def _init_model_adapter(self):
        """初始化模型适配器"""
        try:
            model_config = self.settings.get_model_config()
            self.model_adapter = ModelFactory.create_adapter(
                provider=model_config["provider"],
                api_key=model_config["api_key"],
                api_url=model_config["api_url"],
                model=model_config["model"]
            )

            if not self.model_adapter.validate_config():
                error_msg = f"模型配置验证失败 - 提供商: {model_config['provider']}"
                if model_config["api_key"] in ["your-deepseek-api-key-here", "your-openai-api-key-here", "your-api-key-here"]:
                    error_msg += "\n❌ 检测到占位符API密钥，请在.env文件中配置真实的API密钥"
                logger.error(error_msg)
                raise ValueError(error_msg)

            logger.info(
                f"LLMService initialized with Provider: {self.model_adapter.get_provider_name()}")

        except Exception as e:
            logger.error(f"初始化模型适配器失败: {str(e)}")
            self.model_adapter = None

    async def generate_test_cases(self, request: TestCaseRequest) -> TestCaseResponse:
        """生成测试用例 - 主入口方法"""
        requirement_length = len(request.requirement or "")
        logger.info(f"开始生成测试用例，需求长度: {requirement_length} 字符")

        # 第一步：分析需求
        analysis_result = await self.analysis_service.analyze_requirement(request.requirement or "")
        logger.info(
            f"需求分析完成，识别到 {len(analysis_result.get('modules', []))} 个功能模块")

        # 第二步：基于分析结果生成测试用例
        return await self._generate_test_cases_from_analysis(request, analysis_result)

    async def _generate_test_cases_from_analysis(self, request: TestCaseRequest,
                                                 analysis_result: Dict[str, Any]) -> TestCaseResponse:
        """基于需求分析结果生成测试用例"""
        modules = analysis_result.get('modules', [])

        if not modules:
            logger.warning("需求分析未识别到功能模块，使用传统方式生成")
            return await self._generate_test_cases_single(request)

        # 智能选择生成策略
        total_content_length = sum(len(module.get(
            'description', '') + module.get('test_points', '')) for module in modules)

        # 优化策略：根据模块数量和内容复杂度选择策略
        if len(modules) > 5 or total_content_length > 5000:
            logger.info(
                f"识别到 {len(modules)} 个模块，总内容长度 {total_content_length}，采用分模块生成策略")
            return await self._generate_test_cases_by_modules(request, modules)
        elif len(modules) <= 3 and total_content_length <= 2000:
            logger.info(f"识别到 {len(modules)} 个小模块，采用整体生成策略（优化：减少API调用）")
            return await self._generate_test_cases_with_analysis(request, analysis_result)
        else:
            logger.info(f"识别到 {len(modules)} 个中等模块，采用智能合并策略")
            return await self._generate_test_cases_with_smart_merge(request, modules)

    async def _generate_test_cases_single(self, request: TestCaseRequest) -> TestCaseResponse:
        """单次生成测试用例"""
        max_retries = 3
        requirement_length = len(request.requirement or "")

        for attempt in range(max_retries):
            try:
                # 获取模型信息用于判断是否为推理模型
                model_name = getattr(self.model_adapter,
                                     'model', '') if self.model_adapter else ""

                # 检查是否为推理模型
                from app.services.llm.reasoning_response_processor import ReasoningResponseProcessor
                reasoning_processor = ReasoningResponseProcessor()
                is_reasoning_model = reasoning_processor.is_reasoning_model(
                    model_name)

                # 根据模型类型计算超时时间
                if is_reasoning_model:
                    timeout = self.timeout_manager.calculate_reasoning_model_timeout(
                        requirement_length, attempt)
                    logger.info(
                        f"推理模型 {model_name} - 开始生成测试用例 (尝试 {attempt + 1}/{max_retries}，推理模型专用超时: {timeout}秒)")
                else:
                    timeout = self.timeout_manager.calculate_smart_timeout(
                        requirement_length, attempt)
                    logger.info(
                        f"普通模型 {model_name} - 开始生成测试用例 (尝试 {attempt + 1}/{max_retries}，标准超时: {timeout}秒)")

                # 生成提示词，传递模型信息
                provider_name = self.model_adapter.get_provider_name() if self.model_adapter else ""
                model_name = getattr(self.model_adapter,
                                     'model', '') if self.model_adapter else ""
                prompt = self.prompt_manager.create_prompt(
                    request, provider_name, model_name)
                logger.info(f"提示词长度: {len(prompt)} 字符")

                # 获取智能提示词配置
                smart_config = self.prompt_manager.get_smart_config(request)

                # 优先使用智能提示词系统的max_tokens配置
                if smart_config and 'max_tokens' in smart_config:
                    max_tokens = smart_config['max_tokens']
                    logger.info(f"使用智能提示词系统配置的max_tokens: {max_tokens}")
                    if smart_config.get('reasoning_model'):
                        logger.info(f"推理模型检测到，使用大token配置: {max_tokens}")
                else:
                    # 回退到传统计算方式
                    max_tokens = self.timeout_manager.calculate_max_tokens(
                        requirement_length)
                    logger.info(f"使用传统max_tokens计算: {max_tokens}")

                # 调用AI模型
                content = await self._call_ai_model(prompt, max_tokens, timeout)

                # 解析响应，传递模型信息
                # 创建增强的请求对象，包含模型信息
                enhanced_request = request
                if hasattr(enhanced_request, '__dict__'):
                    enhanced_request.__dict__['model_name'] = model_name
                    enhanced_request.__dict__['provider_name'] = provider_name

                response = self.response_parser.parse_response(
                    content, enhanced_request)

                # 检查生成的用例数量
                if self._validate_response(response, requirement_length):
                    return response

                if attempt == max_retries - 1:
                    logger.warning("已达到最大重试次数，返回当前结果")
                    return response

            except Exception as e:
                logger.error(f"第{attempt+1}次尝试失败: {str(e)}")

                if attempt == max_retries - 1:
                    logger.error("所有重试均失败，尝试使用回退策略")
                    # 尝试回退策略
                    try:
                        fallback_response = await self._generate_fallback_response(request, str(e))
                        if fallback_response and len(fallback_response.test_cases) > 0:
                            logger.info(
                                f"回退策略成功，生成了{len(fallback_response.test_cases)}个测试用例")
                            return fallback_response
                    except Exception as fallback_error:
                        logger.error(f"回退策略也失败了: {str(fallback_error)}")

                    # 如果回退策略也失败，返回空响应而不是抛出异常
                    logger.warning("返回空测试用例列表")
                    return TestCaseResponse(
                        test_cases=[],
                        test_scenarios=[],
                        test_types=["functional"],
                        test_executors=["qa"],
                        test_design_methods=["use_case"],
                        estimated_effort="0分钟",
                        requirement=request.requirement or "",
                        project_name=request.project_name or "",
                        module_name=request.module_name or "",
                        error_message=f"生成失败: {str(e)}"
                    )

                # 等待后重试
                delay = self.timeout_manager.get_retry_delay(attempt)
                logger.info(f"等待 {delay} 秒后重试...")
                await asyncio.sleep(delay)

        # 不应该到达这里
        raise Exception("生成测试用例失败")

    async def _call_ai_model(self, prompt: str, max_tokens: int, timeout: int) -> str:
        """调用AI模型"""
        if not self.model_adapter:
            raise ValueError("模型适配器未初始化")

        messages = self.prompt_manager.get_system_messages()
        messages.append({"role": "user", "content": prompt})

        content = await self.model_adapter.generate_completion(
            messages=messages,
            temperature=0.2,
            max_tokens=max_tokens,
            timeout=timeout
        )

        logger.info(f"✅ 模型适配器调用成功，返回内容长度: {len(content)}")
        logger.info(f"AI返回内容长度: {len(content)}")
        logger.info(f"AI返回原始内容前500字符: {content[:500]}")

        # 添加调试日志
        if hasattr(logger, 'debug'):
            logger.debug(f"AI返回完整内容: {content}")

        return content

    async def _generate_fallback_response(self, request: TestCaseRequest, error_msg: str) -> Optional[TestCaseResponse]:
        """生成回退响应"""
        logger.info("尝试使用回退策略生成测试用例")

        try:
            # 尝试使用简化的提示词
            simplified_prompt = self._create_simplified_prompt(request)

            # 使用更短的超时时间
            fallback_timeout = 60
            max_tokens = 1500

            messages = [{"role": "user", "content": simplified_prompt}]

            content = await self.model_adapter.generate_completion(
                messages=messages,
                temperature=0.3,
                max_tokens=max_tokens,
                timeout=fallback_timeout
            )

            # 使用回退解析
            response = self.response_parser._fallback_parse(content, request)

            if response and len(response.test_cases) > 0:
                logger.info(f"回退策略成功生成{len(response.test_cases)}个测试用例")
                return response

        except Exception as e:
            logger.error(f"回退策略失败: {str(e)}")

        return None

    def _create_simplified_prompt(self, request: TestCaseRequest) -> str:
        """创建简化的提示词"""
        requirement = request.requirement or "基本功能测试"

        return f"""请为以下需求生成5个基本测试用例，返回JSON格式：

需求：{requirement[:500]}

请返回JSON格式：
{{
  "test_cases": [
    {{
      "id": "TC001",
      "title": "测试用例标题",
      "preconditions": "前置条件",
      "steps": [
        {{"step_number": 1, "description": "操作步骤", "expected_result": "预期结果"}}
      ],
      "priority": "P1"
    }}
  ]
}}"""

    def _validate_response(self, response: TestCaseResponse, requirement_length: int) -> bool:
        """验证响应质量"""
        # 根据需求长度确定最低用例数要求
        if requirement_length > 3000:
            min_required_cases = 20
        elif requirement_length > 1500:
            min_required_cases = 15
        elif requirement_length > 800:
            min_required_cases = 10
        elif requirement_length > 300:
            min_required_cases = 8
        else:
            min_required_cases = 5

        actual_cases = len(response.test_cases)

        if actual_cases >= min_required_cases:
            logger.info(
                f"✅ 生成了{actual_cases}个用例，满足最低要求{min_required_cases}个，直接返回")
            return True
        elif actual_cases >= 5:
            logger.info(f"✅ 生成了{actual_cases}个用例，达到最低要求5个，直接返回")
            return True
        else:
            logger.warning(f"⚠️ 生成的用例数量不足({actual_cases}个)，最低需要5个")
            return False

    async def _generate_test_cases_by_modules(self, request: TestCaseRequest,
                                              modules: List[Dict[str, Any]]) -> TestCaseResponse:
        """分模块生成测试用例"""
        # 检查是否启用并发处理
        enable_concurrent = getattr(
            self.settings, 'enable_concurrent_processing', False)

        if enable_concurrent and len(modules) > 1:
            return await self._generate_concurrent(request, modules)
        else:
            return await self._generate_serial(request, modules)

    async def _generate_serial(self, request: TestCaseRequest,
                               modules: List[Dict[str, Any]]) -> TestCaseResponse:
        """串行生成测试用例"""
        all_test_cases = []
        all_scenarios = []
        total_modules = len(modules)

        logger.info(f"使用串行处理，共{total_modules}个模块")

        for i, module in enumerate(modules):
            module_name = module.get('name', f'模块{i+1}')
            logger.info(f"生成第 {i+1}/{total_modules} 个模块的测试用例: {module_name}")

            try:
                # 为模块创建请求
                module_request = self._create_module_request(module, request)

                # 生成模块测试用例
                module_response = await self._generate_test_cases_single(module_request)

                all_test_cases.extend(module_response.test_cases)
                all_scenarios.extend(module_response.test_scenarios)

                # 智能延迟
                if i < len(modules) - 1:
                    await asyncio.sleep(2)

            except Exception as e:
                logger.error(f"生成模块 {module_name} 的测试用例失败: {str(e)}")
                continue

        # 重新编号测试用例
        for i, test_case in enumerate(all_test_cases):
            test_case.id = f"TC{i+1:03d}"

        return TestCaseResponse(
            test_cases=all_test_cases,
            test_scenarios=all_scenarios,
            test_types=["functional"],
            test_executors=["qa"],
            test_design_methods=["use_case"],
            estimated_effort=f"约{len(all_test_cases) * 30}分钟",
            requirement=request.requirement or "",
            project_name=request.project_name or "",
            module_name=request.module_name or ""
        )

    async def _generate_concurrent(self, request: TestCaseRequest,
                                   modules: List[Dict[str, Any]]) -> TestCaseResponse:
        """并发生成测试用例"""
        logger.info(f"启用并发处理，共{len(modules)}个模块")

        # 延迟导入并发处理器
        if self.concurrent_processor is None:
            from ..concurrent_processor import ConcurrentProcessor
            self.concurrent_processor = ConcurrentProcessor(self)

        return await self.concurrent_processor.process_modules_concurrently(modules, request)

    async def _generate_test_cases_with_smart_merge(self, request: TestCaseRequest,
                                                    modules: List[Dict[str, Any]]) -> TestCaseResponse:
        """智能合并策略生成测试用例"""
        logger.info(f"使用智能合并策略处理 {len(modules)} 个模块")

        # 将模块按相似性和大小进行分组
        merged_groups = self._merge_similar_modules(modules)
        logger.info(
            f"模块合并后分为 {len(merged_groups)} 组，预计减少 {len(modules) - len(merged_groups)} 次API调用")

        all_test_cases = []
        all_scenarios = []

        for i, group in enumerate(merged_groups):
            group_name = f"合并组{i+1}"
            logger.info(
                f"处理第 {i+1}/{len(merged_groups)} 组: {group_name}，包含 {len(group)} 个模块")

            try:
                # 为合并组创建请求
                merged_request = self._create_merged_request(group, request)

                # 生成合并组的测试用例
                group_response = await self._generate_test_cases_single(merged_request)

                all_test_cases.extend(group_response.test_cases)
                all_scenarios.extend(group_response.test_scenarios)

                # 组间延迟
                if i < len(merged_groups) - 1:
                    await asyncio.sleep(1)

            except Exception as e:
                logger.error(f"生成合并组 {group_name} 的测试用例失败: {str(e)}")
                continue

        # 重新编号测试用例
        for i, test_case in enumerate(all_test_cases):
            test_case.id = f"TC{i+1:03d}"

        return TestCaseResponse(
            test_cases=all_test_cases,
            test_scenarios=all_scenarios,
            test_types=["functional"],
            test_executors=["qa"],
            test_design_methods=["use_case"],
            estimated_effort=f"约{len(all_test_cases) * 30}分钟",
            requirement=request.requirement or "",
            project_name=request.project_name or "",
            module_name=request.module_name or ""
        )

    def _merge_similar_modules(self, modules: List[Dict[str, Any]]) -> List[List[Dict[str, Any]]]:
        """将相似的模块合并为组"""
        if len(modules) <= 2:
            return [modules]

        groups = []
        remaining_modules = modules.copy()

        while remaining_modules:
            current_group = [remaining_modules.pop(0)]
            current_group_size = len(current_group[0].get('description', ''))

            # 查找可以合并的模块
            i = 0
            while i < len(remaining_modules) and len(current_group) < 3:  # 每组最多3个模块
                module = remaining_modules[i]
                module_size = len(module.get('description', ''))

                # 检查是否可以合并（基于大小和相似性）
                if (current_group_size + module_size < 2000 and
                        self._are_modules_similar(current_group[0], module)):
                    current_group.append(remaining_modules.pop(i))
                    current_group_size += module_size
                else:
                    i += 1

            groups.append(current_group)

        return groups

    def _are_modules_similar(self, module1: Dict[str, Any], module2: Dict[str, Any]) -> bool:
        """检查两个模块是否相似"""
        try:
            name1 = module1.get('name', '').lower()
            name2 = module2.get('name', '').lower()

            # 简单的相似性检查：基于模块名称的关键词重叠
            words1 = set(name1.split())
            words2 = set(name2.split())

            if not words1 or not words2:
                return False

            intersection = words1.intersection(words2)
            union = words1.union(words2)

            similarity = len(intersection) / len(union) if union else 0

            return similarity >= 0.3  # 30%相似度阈值

        except Exception:
            return False

    def _create_merged_request(self, modules: List[Dict[str, Any]], base_request: TestCaseRequest) -> TestCaseRequest:
        """为合并的模块组创建请求"""
        # 合并模块描述
        merged_description = "\n\n".join([
            f"模块：{module.get('name', '未命名模块')}\n描述：{module.get('description', '')}\n测试点：{module.get('test_points', '')}"
            for module in modules
        ])

        # 合并模块名称
        module_names = [module.get('name', '未命名模块') for module in modules]
        merged_module_name = " + ".join(module_names[:2])  # 只显示前两个模块名
        if len(module_names) > 2:
            merged_module_name += f" 等{len(module_names)}个模块"

        return TestCaseRequest(
            requirement=merged_description,
            project_name=base_request.project_name,
            module_name=merged_module_name,
            additional_info=base_request.additional_info,
            test_types=base_request.test_types,
            test_executors=base_request.test_executors,
            test_design_methods=base_request.test_design_methods
        )

    def _create_module_request(self, module: Dict[str, Any], original_request: TestCaseRequest) -> TestCaseRequest:
        """为单个模块创建请求"""
        module_requirement = self.prompt_manager.create_module_prompt(
            module, original_request.requirement)

        return TestCaseRequest(
            requirement=module_requirement,
            project_name=original_request.project_name,
            module_name=module.get('name', '功能模块'),
            additional_info=original_request.additional_info
        )

    async def _generate_test_cases_with_analysis(self, request: TestCaseRequest,
                                                 analysis_result: Dict[str, Any]) -> TestCaseResponse:
        """基于完整分析结果生成测试用例"""
        enhanced_request = self._enhance_request_with_analysis(
            request, analysis_result)
        return await self._generate_test_cases_single(enhanced_request)

    def _enhance_request_with_analysis(self, request: TestCaseRequest,
                                       analysis_result: Dict[str, Any]) -> TestCaseRequest:
        """使用分析结果增强请求"""
        modules = analysis_result.get('modules', [])
        test_points = analysis_result.get('test_points', [])
        business_rules = analysis_result.get('business_rules', [])
        constraints = analysis_result.get('constraints', [])

        enhanced_parts = [
            "【需求分析结果】",
            f"原始需求：{request.requirement}",
            ""
        ]

        if modules:
            enhanced_parts.append("【功能模块】")
            for i, module in enumerate(modules, 1):
                enhanced_parts.append(f"{i}. {module.get('name', '未知模块')}")
                enhanced_parts.append(f"   描述：{module.get('description', '')}")
                if module.get('test_points'):
                    enhanced_parts.append("   测试点：")
                    for point in module['test_points']:
                        enhanced_parts.append(f"   - {point}")
                enhanced_parts.append("")

        if test_points:
            enhanced_parts.append("【关键测试点】")
            for point in test_points:
                enhanced_parts.append(f"- {point.get('point', '')}")
            enhanced_parts.append("")

        if business_rules:
            enhanced_parts.append("【业务规则】")
            for rule in business_rules:
                enhanced_parts.append(f"- {rule}")
            enhanced_parts.append("")

        if constraints:
            enhanced_parts.append("【约束条件】")
            for constraint in constraints:
                enhanced_parts.append(f"- {constraint}")

        enhanced_requirement = "\n".join(enhanced_parts)

        return TestCaseRequest(
            requirement=enhanced_requirement,
            project_name=request.project_name,
            module_name=request.module_name,
            additional_info=request.additional_info
        )
